/****************************************
File: framework.cpp

This file is part of Stellar

Copyright (C) 2001 David Stibbe (dstibbe@gmail.com)
                     and Gerrit Jan Jansen

Stellar is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
****************************************/

#include "framework.h"
#include "opdracht6.h"



Fl_Button *playButton = NULL;
Fl_Button *stopButton = NULL;
Fl_Button *zoomOutButton = NULL;
Fl_Button *zoomInButton = NULL;
Fl_Button *turnRightButton = NULL;
Fl_Button *turnLeftButton = NULL;
Fl_Button *turnUpButton = NULL;
Fl_Button *turnDownButton = NULL;

GLWindow *openglWindow = NULL;
Fl_Value_Slider *slider1Widget = NULL;
Fl_Value_Slider *slider2Widget = NULL;
Fl_Value_Slider *slider3Widget = NULL;

float slider1 = 1.0f, slider2 = 1.0f, slider3 = 1.0f;	// de waardes van de sliders
int running = 0;    // true als de 'animatie' speelt

int numberOfVertices;	// aantal vertices in het model
vertex *vertices;			// alle vertices van het model
int numberOfFaces;		// aantal faces in het model
face *faces;				// alle faces van het model


int readModel(const char *filename) {
	int i;

	printf("Going to read model ...\n");
	FILE *F = fopen(filename, "r");
	if (F == NULL) {
		fprintf(stderr, "readModel(): could not open %s\n", filename);
		return 0;
	}
	printf("Going to to determine Vertices ...\n");

	if (fscanf(F, "%d\n", &numberOfVertices) != 1) {
		fprintf(stderr, "readModel(): could not read number of vertices\n");
		fclose(F);
		return 0;
	}

	printf("Going to read ...\n");
	if ( (vertices = (vertex*)malloc(sizeof(vertex) * numberOfVertices)) == NULL) {
		fprintf(stderr, "readModel(): could not allocate memory for vertices\n");
		fclose(F);
		return 0;
	}


	printf("Going to read ...\n");
	for (i = 0; i < numberOfVertices; i++) {
		if (fscanf(F, "%f %f %f\n", vertices[i].pos + 0, vertices[i].pos + 1, vertices[i].pos + 2) != 3) {
			fprintf(stderr, "readModel(): could not read vertex %d\n", i);
			free(vertices);
			fclose(F);
			return 0;
		}
		vertices[i].normal[0] = vertices[i].normal[1] = vertices[i].normal[2] = 0.0f;
	}


	printf("Going to read ...\n");
	if (fscanf(F, "%d\n", &numberOfFaces) != 1) {
		fprintf(stderr, "readModel(): could not read number of faces\n");
		free(vertices);
		fclose(F);
		return 0;
	}

	printf("Going to read ...\n");
	if ( (faces = (face*)malloc(sizeof(face) * numberOfFaces)) == NULL) {
		fprintf(stderr, "readModel(): could not allocate memory for faces\n");
		free(vertices);
		fclose(F);
		return 0;
	}

	printf("Going to read ...\n");
	for (i = 0; i < numberOfFaces; i++) {
		if (fscanf(F, "%d %d %d\n", faces[i].vertex + 0, faces[i].vertex + 1, faces[i].vertex + 2) == 0) {
			fprintf(stderr, "readModel(): could not read face %d\n", i);
			free(vertices);
			free(faces);
			fclose(F);
			return 0;
		}
		faces[i].normal[0] = faces[i].normal[1] = faces[i].normal[2] = 0.0f;
	}

	fclose(F);

    printf("Going to load ...\n");
	loadModel(numberOfVertices,vertices,numberOfFaces,faces );

	printf("%s: %d vertices, %d faces\n", filename, numberOfVertices, numberOfFaces);

	return 1;
}

void redraw() {
	openglWindow->redraw();
}

void timeoutFunc(void *) {
  if (running) { // Als er op de playButton is gedrukt,
    step();      // doe dan een 'stap' (->verplaats het figuur)
    redraw();    // en teken het figuur opnieuw.
  }

  // Vraag een nieuwe timeout aan voor over 1/25ste seconde
  Fl::add_timeout(1.0 / 25.0, timeoutFunc, NULL);
}

void buttonCallback(Fl_Widget *W, void *Data) {
	if (W == playButton) {
		running = !running;
		playButton->label((running) ? "@||" : "@>");
		playButton->redraw();
	}
	else if (W == stopButton) {
		reset();
		running = 0;
		playButton->label((running) ? "@||" : "@>");
		playButton->redraw();
		redraw();
	}
	else if (W == zoomInButton) {
		zoom(0.9);
		redraw();
	}
	else if (W == zoomOutButton) {
		zoom(1.1);
		redraw();
	}
	else if (W == turnLeftButton) {
		turnCamera(5.0);
		redraw();
	}
	else if (W == turnRightButton) {
		turnCamera(-5.0);
		redraw();
	}
	else if (W == turnUpButton) {
		turnCameraZ(5.0);
		redraw();
	}
	else if (W == turnDownButton) {
		turnCameraZ(-5.0);
		redraw();
	}
	else if (W == slider1Widget) {
		slider1 = (float)slider1Widget->value();
		setScaleFactor(slider1);
		redraw();
	}
	else if (W == slider2Widget) {
		slider2 = (float)slider2Widget->value();
		setTransFactor(slider2);
		redraw();
	}
	else if (W == slider3Widget) {
		slider3 = (float)slider3Widget->value();
		setRotFactor(slider3);
		redraw();
	}
}

void createWindow(int argc, char *argv[]) {
/*
Maak het window aan:
*/
	// Maak een 'top-level' window.
	Fl_Window *window = new Fl_Window(600, 600, "opdracht6");

	// Maak een childwindow (GLWindow)
	openglWindow = new GLWindow(5, 5, 590, 500, "GL Window");
	openglWindow->mode(FL_RGB | FL_DOUBLE | FL_DEPTH);			// stel opengl in op rgb kleur & double buffering

	Fl_Window *controlBarWindow = new Fl_Window(0, 510, 600, 100);
	controlBarWindow->box(FL_DOWN_BOX);

	// maak play button
	playButton = new Fl_Button(5,5,25,25, "@#>");		// @#> + FL_SYMBOL_LABEL zorgt voor het 'play teken'
	playButton->labeltype(FL_SYMBOL_LABEL);
	playButton->align(FL_ALIGN_CLIP);
	playButton->shortcut(FL_CTRL + 'p');				// CTRL-p is de shortcut voor de playbutton
	playButton->callback(buttonCallback, NULL);			// buttonCallback wordt aangeroepen als er op playbutton wordt gedrukt

	// maak stop button
	stopButton = new Fl_Button(35,5,25,25, "@#square");
	stopButton->labeltype(FL_SYMBOL_LABEL);
	stopButton->align(FL_ALIGN_CLIP);
	stopButton->shortcut(FL_CTRL + 's');
	stopButton->callback(buttonCallback, NULL);

	// maak zoomIn button
	zoomInButton = new Fl_Button(5,30,25,25, "+");
	zoomInButton ->align(FL_ALIGN_CLIP);
	zoomInButton ->shortcut('+');
	zoomInButton ->callback(buttonCallback, NULL);

		// maak zoomOut button
	zoomOutButton = new Fl_Button(5,55,25,25, "-");
	zoomOutButton ->align(FL_ALIGN_CLIP);
	zoomOutButton ->shortcut('-');
	zoomOutButton ->callback(buttonCallback, NULL);

	turnRightButton = new Fl_Button(80,55,25,25, ">");
	turnRightButton ->shortcut(FL_Right);
	turnRightButton ->callback(buttonCallback, NULL);


	turnLeftButton = new Fl_Button(30,55,25,25, "<");
	turnLeftButton ->shortcut(FL_Left);
	turnLeftButton ->callback(buttonCallback, NULL);

	turnUpButton = new Fl_Button(55,30,25,25, "/\\");
	turnUpButton ->shortcut(FL_Up);
	turnUpButton ->callback(buttonCallback, NULL);


	turnDownButton = new Fl_Button(55,80,25,25, "\\/");
	turnDownButton ->shortcut(FL_Down);
	turnDownButton ->callback(buttonCallback, NULL);

	// maak 3 sliders
	slider1Widget = new Fl_Value_Slider (65, 5, 175, 25);
	slider1Widget->type(FL_HOR_SLIDER);
	slider1Widget->range(-2.0, 2.0);
	slider1Widget->step(0.01);
	slider1Widget->callback(buttonCallback, NULL);
	slider1Widget->value(slider1);
	setScaleFactor(slider1);

	slider2Widget = new Fl_Value_Slider (245, 5, 175, 25);
	slider2Widget->type(FL_HOR_SLIDER);
	slider2Widget->range(-2.0, 2.0);
	slider2Widget->step(0.01);
	slider2Widget->callback(buttonCallback, NULL);
	slider2Widget->value(slider2);
	setTransFactor(slider2);

	slider3Widget = new Fl_Value_Slider (425, 5, 170, 25);
	slider3Widget->type(FL_HOR_SLIDER);
	slider3Widget->range(-2.0, 2.0);
	slider3Widget->step(0.01);
	slider3Widget->callback(buttonCallback, NULL);
	slider3Widget->value(slider3);
	setRotFactor(slider3);

	controlBarWindow->end();

	// Vertel 'window' dat 'openglWindow' resizable is
	window->resizable(openglWindow);

	// We zijn klaar met 'window'
	window->end();
	// Laat 'window' zien
	window->show(argc, argv);
/*
window done
*/

	// Vraag een timeout aan voor over 1/25ste seconde
	Fl::add_timeout(1.0 / 25.0, timeoutFunc, NULL);
}

int GLWindow::handle(int event) {
	switch (event) {
	case FL_PUSH:
		if (Fl::event_button() == 1) {
			mouseButtonDown((float)Fl::event_x(), h() - (float)Fl::event_y());
			return 1;
		}
		return 0;
	case FL_RELEASE:
		if (Fl::event_button() == 1) {
			mouseButtonUp((float)Fl::event_x(), h() - (float)Fl::event_y());
			return 1;
		}
		return 0;
	case FL_DRAG:
		if (Fl::event_button() == 1) {
			mouseDrag((float)Fl::event_x(), h() - (float)Fl::event_y());
			return 1;
		}
		return 0;
	default:
		return Fl_Gl_Window::handle(event);
	}
}



void GLWindow::draw() {
	beginglDraw();

	doDrawing();

	endglDraw();
}



void GLWindow::beginglDraw() {
	if (!valid()) {
		// setup viewport
		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

		GLsizei width, height;
		GLdouble aspect;

		width = w();
		height = h();

		if ((width == 0) || (height == 0))
		{
		    return;
		}

		if (width==0)
		{
            aspect = (GLdouble)width;
		}
		else
		{
		    aspect = (GLdouble)width/(GLdouble)height;
		}

		glViewport(0, 0, width, height);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(45, aspect, 01, 50.0);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		// set standard opengl stuff
		glEnable(GL_DEPTH_TEST);
	}

#ifndef MESA
	glDrawBuffer(GL_BACK);
#endif // !MESA
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

void GLWindow::endglDraw() {
#ifndef MESA
	glDrawBuffer(GL_BACK);
#endif // !MESA
}
